/*
 * Created on Oct 6, 2005
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.idimitrov.pdftools.pdfchecker;

import java.io.IOException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.lowagie.text.pdf.PRStream;
import com.lowagie.text.pdf.PRTokeniser;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfNumber;
import com.lowagie.text.pdf.PdfObject;
import com.lowagie.text.pdf.PdfReader;
import com.sun.org.apache.xerces.internal.dom.DocumentImpl;

/**
 * Autor: Dmitry Mironovich Date: 09.08.2005
 */

public class ImageManager {

    private boolean m_consistGrayScale;
    private boolean m_consistColor;
    private boolean m_consistBlackAndWhite;
    private Document m_doc;
    private int imgCount = 0;

    private void addImage(String colorSpace, int bitsPerComp, Element root) {
	Element img = m_doc.createElement("image"/* + imgCount */);
	img.setAttribute("sequence", String.valueOf(imgCount));
	Node tn = m_doc.createTextNode(colorSpace);
	Element cs = m_doc.createElement("ColorSpace");
	cs.appendChild(tn);
	Element bpc = m_doc.createElement("BitsPerComponent");
	bpc.appendChild(m_doc.createTextNode(String.valueOf(bitsPerComp)));

	System.out.println("\"" + colorSpace + "\"");

	boolean isGray = bitsPerComp > 1
		&& (colorSpace.equals("DeviceGray")
			|| colorSpace.equals("CalGray")
			|| colorSpace.equals("Indexed/CalGray") || colorSpace
			.equals("Indexed/GrayScale"));
	boolean isColor = bitsPerComp > 1
		&& (!(colorSpace.equals("DeviceGray") || colorSpace
			.equals("CalGray")));
	boolean isBlackAndWhite = bitsPerComp == 1;

	if (isGray) {
	    img.setAttribute("quality", "grayscale");
	} else if (isColor) {
	    img.setAttribute("quality", "color");
	} else {
	    img.setAttribute("quality", "black/white");
	}
	m_consistGrayScale = m_consistGrayScale || isGray;
	m_consistColor = m_consistColor || isColor;
	m_consistBlackAndWhite = m_consistBlackAndWhite || isBlackAndWhite;

	img.appendChild(cs);
	img.appendChild(bpc);
	root.appendChild(img);
	imgCount++;
    }

    /**
     * @param pdfName
     *            link to PDF file
     * @return XML Document files with description of images
     */
    private Document buildImages(String pdfName) throws IOException {
	PdfReader reader = null;
	m_doc = new DocumentImpl();
	m_consistGrayScale = false;
	m_consistColor = false;
	m_consistBlackAndWhite = false;

	try {
	    reader = new PdfReader(pdfName);
	    reader.consolidateNamedDestinations();
	    reader.createFakeFontSubsets();

	    imgCount = 1;
	    Element root = m_doc.createElement("images");
	    for (int n = 1; n < reader.getXrefSize(); ++n) {
		PdfObject obj = reader.getPdfObjectRelease(n);

		if (obj == null) {
		    continue;
		}

		if (obj.isStream()) {
		    PRStream stream = (PRStream) obj;
		    if (stream.contains(PdfName.TYPE)
			    && ((stream.get(PdfName.TYPE))
				    .equals(PdfName.XOBJECT))) {
			if (stream.contains(PdfName.SUBTYPE)
				&& (((PdfName) stream.get(PdfName.SUBTYPE))
					.equals(PdfName.IMAGE))) {
			    obj = stream.get(PdfName.COLORSPACE);
			    String colorSpace = "";
			    while (obj != null && (!(obj.isName()))) {
				if (obj.isIndirect()) {
				    obj = PdfReader.getPdfObjectRelease(obj);
				} else {
				    if (obj.isArray()) {
					PdfArray arr = (PdfArray) obj;
					obj = (PdfObject) arr.getArrayList()
						.get(0);
					if (obj.isName()
						&& (obj.equals(PdfName.INDEXED))) {
					    colorSpace = "Indexed/";
					    obj = (PdfObject) arr
						    .getArrayList().get(1);
					}
				    }

				}
			    }

			    if (obj != null) {
				colorSpace = colorSpace
					+ ((PdfName) obj).toString().substring(
						1);
			    }

			    int bitsPerComp = ((PdfNumber) stream
				    .get(PdfName.BITSPERCOMPONENT)).intValue();
			    addImage(colorSpace, bitsPerComp, root);
			}
		    } else {
			int iStartPos = 0;
			String sStreamContent = new String(PdfReader
				.getStreamBytes(stream));
			// System.out.println(sStreamContent);
			while ((iStartPos = getTokenPos("BI", sStreamContent,
				iStartPos)) != -1) {
			    int iEndPos = getTokenPos("ID", sStreamContent,
				    iStartPos);
			    String sTextBlock = sStreamContent.substring(
				    iStartPos, iEndPos + 2);
			    iStartPos = iStartPos + 2;

			    // System.out.println(sTextBlock);
			    PRTokeniser tk = new PRTokeniser(sTextBlock
				    .getBytes());

			    String operator = "";
			    String colorSpace = "";
			    String name = "";
			    int bitsPerComp = 0;
			    boolean bInArray = false;

			    while (tk.nextToken()) {
				if (tk.getTokenType() == PRTokeniser.TK_OTHER) {
				    operator = tk.getStringValue();
				} else if (tk.getTokenType() == PRTokeniser.TK_START_ARRAY) {
				    bInArray = true;
				    name = operator;
				} else if (tk.getTokenType() == PRTokeniser.TK_END_ARRAY) {
				    bInArray = false;
				} else if (tk.getTokenType() == PRTokeniser.TK_NUMBER) {
				    if (operator.equals("BPC")) {
					System.out.println(bitsPerComp);
					bitsPerComp = tk.intValue();
				    }
				} else if (tk.getTokenType() == PRTokeniser.TK_NAME) {
				    operator = tk.getStringValue();

				    if (name.equals("CS") && !bInArray) {
					colorSpace = colorSpace
						+ tk.getStringValue();
					break;
				    }

				    if (operator.equals("CS")) {
					name = operator;
				    } else if (operator.equals("I")
					    && name.equals("CS") && bInArray) {
					colorSpace = "Indexed/";
					tk.nextToken();
					if (tk.getTokenType() == PRTokeniser.TK_NAME) {
					    colorSpace = colorSpace
						    + tk.getStringValue();
					    break;
					}
				    }

				}
			    }
			    addImage(colorSpace, bitsPerComp, root);
			}

			while ((iStartPos = getTokenPos("BT", sStreamContent,
				iStartPos)) != -1) {
			    int iEndPos = getTokenPos("ET", sStreamContent,
				    iStartPos);
			    String sTextBlock = sStreamContent.substring(
				    iStartPos, iEndPos + 2);
			    iStartPos = iStartPos + 2;
			    PRTokeniser tk = new PRTokeniser(sTextBlock
				    .getBytes());

			    String operator = "";
			    String colorSpace = "";
			    String name = "";
			    int bitsPerComp = 0;
			    boolean bInArray = false;

			    while (tk.nextToken()) {
				if (tk.getTokenType() == PRTokeniser.TK_OTHER) {
				    operator = tk.getStringValue();
				} else if (tk.getTokenType() == PRTokeniser.TK_START_ARRAY) {
				    bInArray = true;
				    name = operator;
				} else if (tk.getTokenType() == PRTokeniser.TK_END_ARRAY) {
				    bInArray = false;
				} else if (tk.getTokenType() == PRTokeniser.TK_NUMBER) {
				    if (operator.equals("BPC")) {
					bitsPerComp = tk.intValue();
				    }
				} else if (tk.getTokenType() == PRTokeniser.TK_NAME) {
				    operator = tk.getStringValue();

				    if (name.equals("CS") && !bInArray) {
					colorSpace = colorSpace
						+ tk.getStringValue();
					break;
				    }

				    if (operator.equals("CS")) {
					name = operator;
				    } else if (operator.equals("I")
					    && name.equals("CS") && bInArray) {
					colorSpace = "Indexed/";
					tk.nextToken();
					if (tk.getTokenType() == PRTokeniser.TK_NAME) {
					    colorSpace = colorSpace
						    + tk.getStringValue();
					    break;
					}
				    }

				}
			    }
			    addImage(colorSpace, bitsPerComp, root);
			}
		    }
		}
	    }
	    m_doc.appendChild(root);
	} catch (IOException e) {
	    throw new IOException(
		    "WARNING: failed to check for grayscale in pdf file "
			    + pdfName + " with message " + e.getMessage(), e);
	}
	return m_doc;
    }

    /**
     * @param pdfName
     *            link to PDF file
     * @return true if PDF file consist gray scale images
     */
    public boolean consistGrayScaleImages(String pdfName) throws IOException {
	buildImages(pdfName);
	return m_consistGrayScale;
    }

    /**
     * @param pdfName
     *            link to PDF file
     * @return true if PDF file consist color images
     */
    public boolean consistColorImages(String pdfName) throws IOException {
	buildImages(pdfName);
	return m_consistColor;
    }

    public boolean consistBlackAndWhiteImages(String pdfName)
	    throws IOException {
	buildImages(pdfName);
	return m_consistBlackAndWhite;
    }

    public boolean consistColorText(String pdfName) throws IOException {
	buildImages(pdfName);
	return m_consistColor;
    }

    /**
     * Returns the position of the token in the text block
     */
    public static int getTokenPos(String sName, String s, int iPos) {
	int i = iPos;
	int iLen = sName.length();

	int i1 = 0, i2 = 0;
	String sCheck = "";
	PRTokeniser tkCheck = null;
	while ((i = s.indexOf(sName, i)) != -1) {
	    i1 = i;
	    i2 = i + iLen;

	    if (i1 > 0) {
		i1 -= 1;
	    }
	    if (i2 < (s.length() - 1)) {
		i2 += 1;
	    }
	    sCheck = s.substring(i1, i2);

	    try {
		tkCheck = new PRTokeniser(sCheck.getBytes());
		if (tkCheck.nextToken()) {
		    if ((tkCheck.getTokenType() == PRTokeniser.TK_OTHER)
			    && (tkCheck.getStringValue().compareTo(sName) == 0)) {
			break;
		    }
		    i += iLen;
		}
	    } catch (IOException e) {
		i += iLen;
	    }
	}
	return i;
    }

}